package org.javahispano.javacup.tacticas_aceptadas.jcrosas.chuky.entrenador;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;

import org.javahispano.javacup.modelo.Comando;
import org.javahispano.javacup.modelo.Constantes;
import org.javahispano.javacup.modelo.JugadorDetalle;
import org.javahispano.javacup.modelo.SituacionPartido;
import org.javahispano.javacup.tacticas_aceptadas.jcrosas.chuky.TacticaChuky;
import org.javahispano.javacup.tacticas_aceptadas.jcrosas.chuky.futbolista.Config;
import org.javahispano.javacup.tacticas_aceptadas.jcrosas.chuky.futbolista.Futbolista;
import org.javahispano.javacup.tacticas_aceptadas.jcrosas.chuky.futbolista.FutbolistaBase;
import org.javahispano.javacup.tacticas_aceptadas.jcrosas.chuky.futbolista.FutbolistaConfig;
import org.javahispano.javacup.tacticas_aceptadas.jcrosas.chuky.futbolista.Oponente;
import org.javahispano.javacup.tacticas_aceptadas.jcrosas.chuky.futbolista.Rol;
import org.javahispano.javacup.tacticas_aceptadas.jcrosas.chuky.util.Balon;
import org.javahispano.javacup.tacticas_aceptadas.jcrosas.chuky.util.PosicionBalon;
import org.javahispano.javacup.tacticas_aceptadas.jcrosas.chuky.util.PosicionCampo;
import org.javahispano.javacup.tacticas_aceptadas.jcrosas.chuky.util.PosicionFutura;
import org.javahispano.javacup.tacticas_aceptadas.jcrosas.chuky.util.matriz.Matriz;
import org.javahispano.javacup.tacticas_aceptadas.jcrosas.chuky.util.matriz.MatrizColumna;
import org.javahispano.javacup.tacticas_aceptadas.jcrosas.chuky.util.matriz.MatrizItem;

/**
 * @author Joan Crosas
 *
 */
public class Entrenador implements VisionPartido {
	private Balon balon;
	private boolean saco;
	private int iteracionesSaque;
	private Libreta anotaciones;
	private ArrayList<Futbolista> futbolistas;
	private ArrayList<Futbolista> futbolistasOrdenados;
	private ArrayList<Oponente> oponentes;
	private LinkedList<Comando> comandos;
	
	private Futbolista futbolistaBalonControlado;
	private Futbolista futbolistaBalonControladoAnterior;
	private ArrayList<Futbolista> futbolistasInterceptanBalon;
	private Oponente oponenteInterceptaBalon;
	private double ataque; // 0..1, va en funci� de la posici� de la pilota 
	
	/**
	 * 
	 */
	public Entrenador( TacticaChuky tactica ) {
		balon = new Balon();
		saco = false;
		iteracionesSaque = 0;
		anotaciones = new Libreta();
		futbolistas = new ArrayList<Futbolista>();
		oponentes = new ArrayList<Oponente>();
		comandos = new LinkedList<Comando>();
		futbolistaBalonControlado = null;
		futbolistaBalonControladoAnterior = null;
		futbolistasInterceptanBalon = new ArrayList<Futbolista>();
		oponenteInterceptaBalon = null;
		ataque = 0.8;
		
		for( int i = 0; i < tactica.getDetalle().getJugadores().length; i++) {
			JugadorDetalle jugador = tactica.getDetalle().getJugadores()[i];
			
			FutbolistaConfig futbolistaConfig = new FutbolistaConfig();
			futbolistaConfig.setNumero( jugador.getNumero() );
			futbolistaConfig.setIndice( i );
			futbolistaConfig.setVelocidad( Constantes.getVelocidad( jugador.getVelocidad() ) );
			futbolistaConfig.setPrecision( Constantes.getErrorAngular( jugador.getPresicion() ) * Math.PI );
			futbolistaConfig.setRemate( Constantes.getVelocidadRemate( jugador.getRemate() ) );
			futbolistaConfig.setPosicionDefensiva( new PosicionCampo( tactica.getPosicionDefensiva()[i] ) );
			futbolistaConfig.setPosicionOfensiva( new PosicionCampo( tactica.getPosicionOfensiva()[i] ) );
			futbolistaConfig.setVisionPartido( this );
			
			
			if( i == 0 ) { // 1 portero
				futbolistaConfig.setRol( Rol.PORTERO );
				futbolistaConfig.setImportancia( jugador.getVelocidad() + jugador.getRemate() + 
						jugador.getPresicion() + FutbolistaBase.IMPORTANCIA_PORTERO );
			} else if( i > 0 && i <= 2 ) { // 2 defensas
				futbolistaConfig.setRol( Rol.DEFENSA );
				futbolistaConfig.setImportancia( jugador.getVelocidad() + jugador.getRemate() + 
						jugador.getPresicion() + FutbolistaBase.IMPORTANCIA_DEFENSA );
			} else if( i > 2 && i <= 4 ) { // 2 laterales
				futbolistaConfig.setRol( Rol.LATERAL );
				futbolistaConfig.setImportancia( jugador.getVelocidad() + jugador.getRemate() + 
						jugador.getPresicion() + FutbolistaBase.IMPORTANCIA_LATERAL );
			} else if( i > 4 && i <= 8 ) { // 4 centrales
				futbolistaConfig.setRol( Rol.CENTRO );
				futbolistaConfig.setImportancia( jugador.getVelocidad() + jugador.getRemate() + 
						jugador.getPresicion() + FutbolistaBase.IMPORTANCIA_CENTRO );
			} else { // 2 delanteros
				futbolistaConfig.setRol( Rol.DELANTERO );
				futbolistaConfig.setImportancia( jugador.getVelocidad() + jugador.getRemate() + 
						jugador.getPresicion() + FutbolistaBase.IMPORTANCIA_DELANTERO );
			}
			
			futbolistas.add( new Futbolista( futbolistaConfig ) );
		}
		
		futbolistasOrdenados = new ArrayList<Futbolista>();
		futbolistasOrdenados.addAll( futbolistas );
		futbolistasOrdenados.remove( 0 ); // nos cargamos al portero
		Collections.sort( futbolistasOrdenados ); // ordenamos por importancia
		
	}
	
	public void evaluar( SituacionPartido situacionPartido ) {
		if( oponentes.size() == 0 ) {
			for( int i = 0; i < 11; i++ ) {
				JugadorDetalle[] detalle = situacionPartido.detalleJugadoresRivales();
				Config config = new Config();
				config.setIndice( i );
				config.setNumero( detalle[i].getNumero() );
				config.setPrecision( Constantes.getErrorAngular( detalle[i].getPresicion() ) );
				config.setRemate( Constantes.getVelocidadRemate( detalle[i].getRemate() ) );
				config.setVelocidad( Constantes.getVelocidad( detalle[i].getVelocidad() ) );
				config.setRol( detalle[i].esPortero()? Rol.PORTERO: Rol.OPONENTE );
				oponentes.add( new Oponente( config ) );
			}	
		}
		
		
		
		// actualizamos la posici�n del bal�n
		if( situacionPartido.saco() || situacionPartido.sacaRival() ) {
			balon.resetear();
		}
		balon.nuevaPosicion( new PosicionBalon( situacionPartido.balon(), situacionPartido.alturaBalon() ) );
		
		saco = situacionPartido.saco();
		if( saco ) {
			iteracionesSaque++;
		} else {
			iteracionesSaque = 0;
		}
		
		// limpiamos las variables
		comandos.clear();
		anotaciones.mover();
		futbolistaBalonControlado = null;
		futbolistasInterceptanBalon.clear();
		oponenteInterceptaBalon = null;
		
		
		// actualizamos las posiciones de nuestros jugadores
		for( int i = 0; i < situacionPartido.misJugadores().length; i++ ) {
			futbolistas.get( i ).setPosicion( new PosicionCampo( situacionPartido.misJugadores()[i] ) );
			futbolistas.get( i ).setIteracionesParaRematar( situacionPartido.iteracionesParaRematar()[i] );
		}
		
		// actualizamos las posiciones de los oponentes
		for( int i = 0; i < situacionPartido.rivales().length; i++ ) {
			oponentes.get( i ).setPosicion( new PosicionCampo( situacionPartido.rivales()[i] ) );
			oponentes.get( i ).setIteracionesParaRematar( situacionPartido.iteracionesParaRematarRival()[i] );
			oponentes.get( i ).updateBalon( balon );
		}
		
		// comprobamos si alguno de los oponentes puede interceptar el bal�n
		PosicionFutura posFuturaOp = null;
		for( int i = 0; i < oponentes.size(); i++ ) {
			posFuturaOp = oponentes.get( i ).puedoInterceptarBalon();
			if( posFuturaOp != null ) {
				if( oponenteInterceptaBalon == null ) {
					oponenteInterceptaBalon = oponentes.get( i );
				} else if( posFuturaOp.getTiempo() < oponenteInterceptaBalon.puedoInterceptarBalon().getTiempo() ) {
					oponenteInterceptaBalon = oponentes.get( i );
				}
			}
		}
		
		// comprobamos los futbolistas que pueden interceptar balon
		for( int i = 0; i < futbolistasOrdenados.size(); i++ ) {
			if( futbolistasOrdenados.get(i).puedoInterceptarBalon() != null ) {
				futbolistasInterceptanBalon.add( futbolistasOrdenados.get(i) );
			}
		}
		Collections.sort( futbolistasInterceptanBalon, new Comparator<Futbolista>() {
			public int compare( Futbolista futbolista1, Futbolista futbolista2 ) {
				return futbolista1.puedoInterceptarBalon().getTiempo() - futbolista2.puedoInterceptarBalon().getTiempo();
			}
		});
		while( futbolistasInterceptanBalon.size() > FutbolistaBase.FUTBOLISTAS_MAX_PRESION_DEFENSIVA ) {
			futbolistasInterceptanBalon.remove( FutbolistaBase.FUTBOLISTAS_MAX_PRESION_DEFENSIVA );
		}
		
		// comprobamos si hay uno de los nuestros que tiene el control del bal�n
		for( int i = 0; i < futbolistas.size() && futbolistaBalonControlado == null; i++ ) {
			if( futbolistas.get( i ).getIteracionesParaRematar() == 0 && 
					futbolistas.get( i ).balonEncima( balon ) ) {
				futbolistaBalonControlado = futbolistas.get( i );
				
			} else if( futbolistas.get( i ).getIteracionesParaRematar() > 0 ) {
				PosicionFutura posicionIntercetaBalon = futbolistas.get( i ).puedoInterceptarBalon(); 
				if( posicionIntercetaBalon != null && posicionIntercetaBalon.getTiempo() < Constantes.ITERACIONES_GOLPEAR_BALON ) {
					futbolistaBalonControlado = futbolistas.get( i );
				}
			}
		}
		
		
		// reestablecemos las restricciones de los pases
		if( futbolistaBalonControlado != null && futbolistaBalonControlado != futbolistaBalonControladoAnterior ) {
			resetRestriccionesPase();
		}
		if( futbolistaBalonControlado != null ) {
			futbolistaBalonControladoAnterior = futbolistaBalonControlado;
		}
		

		// preparamos la defensa
		if( situacionPartido.iteracion() % 10 == 0  ) { 
			prepararDefensa();
		}
		
		
		
		for( int i = 0; i < futbolistas.size(); i++ ) {
			futbolistas.get( i ).evaluar();
		}
		
		for( int i = 0; i < anotaciones.getSugerencias().size(); i++ ) {
			anotaciones.getSugerencias().get( i ).evaluar();
		}
		
		
		
	}
	
	
	private void prepararDefensa() {
		ArrayList<Futbolista> defensas = new ArrayList<Futbolista>();
		ArrayList<Oponente> rivales = new ArrayList<Oponente>();
		
		// reseteamos la asignacion anterior
		for( int i = 0; i < futbolistas.size(); i++ ) {
			futbolistas.get( i ).asignarOponente( null, 0 );
		}
		for( int i = 0; i < oponentes.size(); i++ ) {
			oponentes.get( i ).getDefensasAsignados().clear();
		}
		
		// reordenamos los oponentes por distancia a porteria
		ArrayList<Oponente> oponentesOrdenados = new ArrayList<Oponente>();
		oponentesOrdenados.addAll( oponentes );
		Collections.sort( oponentesOrdenados );
		
		//double distanciaDefensiva = FutbolistaBase.DISTANCIA_DEFENSIVA;
		double distanciaBalonPorteria = balon.getPosicionBalon().distancia( new PosicionCampo( Constantes.centroArcoInf ) );
		double distanciaDefensiva = distanciaBalonPorteria + 22;
		distanciaDefensiva = Math.min( FutbolistaBase.DISTANCIA_DEFENSIVA, distanciaDefensiva );
		distanciaDefensiva = Math.max( FutbolistaBase.DISTANCIA_DEFENSIVA_MEDIA, distanciaDefensiva );
		ataque = ( distanciaDefensiva - FutbolistaBase.DISTANCIA_DEFENSIVA_MEDIA ) / 
			( FutbolistaBase.DISTANCIA_DEFENSIVA - FutbolistaBase.DISTANCIA_DEFENSIVA_MEDIA );
		
		Futbolista defensa = null;
		for( int i = 0; i < oponentesOrdenados.size(); i++ ) {
			if( oponentesOrdenados.get( i ).getDistanciaPorteria() < FutbolistaBase.DISTANCIA_DEFENSIVA_CRITICA ) {
				defensa = futbolistaMasCercano( oponentesOrdenados.get( i ).getPosicion(), Rol.DEFENSA, defensas );
			}
			if( defensa == null && oponentesOrdenados.get( i ).getDistanciaCorner() < FutbolistaBase.DISTANCIA_DEFENSIVA_LATERAL ) {
				defensa = futbolistaMasCercano( oponentesOrdenados.get( i ).getPosicion(), Rol.LATERAL, defensas );
			}
			if( defensa == null && oponentesOrdenados.get( i ).getDistanciaPorteria() < distanciaDefensiva ) {
				defensa = futbolistaMasCercano( oponentesOrdenados.get( i ).getPosicion(), Rol.CENTRO, defensas );
			}
			if( defensa != null ) {
				defensas.add( defensa );
				defensa = null;
				rivales.add( oponentesOrdenados.get( i ) );
			} else {
				break;
			}
		}
		
		if( defensas.size() == 0 ) return;
		
		// els centrals defensen sempre
		int numDefensasDoblados = 0;
		while( (defensa = futbolistaRol( Rol.CENTRO, defensas )) != null ) {
			defensas.add( defensa );
			numDefensasDoblados++;
		}
		
		// doblamos los rivales mas peligrosos en funcion de los defensas
		ArrayList<Oponente> rivalesDoblados = new ArrayList<Oponente>();
		for( int i = 0; rivalesDoblados.size() < numDefensasDoblados; i++ ) {
			if( i >= rivales.size()  ) {
				i = 0;
			}
			rivalesDoblados.add( rivales.get( i ) );
		}
		rivales.addAll( rivalesDoblados );
		
		// preparamos la matriz defensiva
		Matriz matrizDefensiva = new Matriz();
		for( int i = 0; i < defensas.size(); i++ ) {
			MatrizColumna columnaDefensas = new MatrizColumna();
			for( int j = 0; j < rivales.size(); j++ ) {
				Futbolista futbolista = defensas.get( i );
				Oponente oponente = rivales.get( j );
				double distancia = futbolista.getPosicionBaseDefensiva().distancia( oponente.getPosicion() );
				
				MatrizItem asignacion = new MatrizItem( futbolista, oponente, distancia );
				columnaDefensas.addItem( asignacion );
			}
			matrizDefensiva.addColumna( columnaDefensas );
		}
		
		// obtenemos la asignaci�n de jugadores
		MatrizColumna asignaciones = matrizDefensiva.min();
		
		// asignamos
		for( int i = 0; i < asignaciones.size(); i++ ) {
			MatrizItem asignacion = asignaciones.getItem( i );
			defensa = null;
			Oponente rival = null;
			if( asignacion.getObjectX() instanceof Futbolista ) {
				defensa = (Futbolista) asignacion.getObjectX();
				rival = (Oponente) asignacion.getObjectY();
				
			} else if( asignacion.getObjectX() instanceof Oponente ) {
				defensa = (Futbolista) asignacion.getObjectY();
				rival = (Oponente) asignacion.getObjectX();
			}
			defensa.asignarOponente( rival, rival.getDefensasAsignados().size() % 4 );
			rival.getDefensasAsignados().add( defensa );
		}
	}
	
	@Override
	public Futbolista futbolistaMasCercano( PosicionCampo posicion, Rol rol, ArrayList<Futbolista> descartes ) {
		ArrayList<Futbolista> futbolistasCercanos = new ArrayList<Futbolista>();
		for( int i = 0; i < futbolistas.size(); i++ ) {
			if( futbolistas.get( i ).getRol() == rol && !descartes.contains( futbolistas.get(i) ) ) {
				futbolistasCercanos.add( futbolistas.get(i) );
			}
		}
		
		Futbolista futbolistaCercano = null;
		for( int i = 0; i < futbolistasCercanos.size(); i++ ) {
			if( futbolistaCercano == null ) {
				futbolistaCercano = futbolistasCercanos.get(i);
			} else if( futbolistasCercanos.get( i ).getPosicion().distancia( posicion ) <
					futbolistaCercano.getPosicion().distancia( posicion ) ) {
				futbolistaCercano = futbolistasCercanos.get( i );
			}
		
		}
		
		return futbolistaCercano;
	}
	
	public Futbolista futbolistaRol( Rol rol, ArrayList<Futbolista> descartes ) {
		for( int i = 0; i < futbolistas.size(); i++ ) {
			if( futbolistas.get( i ).getRol() == rol && !descartes.contains( futbolistas.get(i) ) ) {
				return futbolistas.get(i);
			}
		}
		return null;
	}

	/**
	 * Funci�n que analiza la seguridad de un jugador. 
	 * @return Devuelve el tiempo (numero iteraciones) que tarad�n los ribales en alcanzar el bal�n
	 */
	@Override
	public int seguridadBalon() {
		int t = 100;
		PosicionFutura posFuturaOp = null;
		for( int i = 0; i < oponentes.size(); i++ ) {
			posFuturaOp = oponentes.get( i ).puedoInterceptarBalon();
			if( posFuturaOp != null && posFuturaOp.getTiempo() < t ) {
				t = posFuturaOp.getTiempo();
			}
		}
		return t;
	}
	
	
	
	@Override
	public ArrayList<Futbolista> futbolistasPuedenInterceptarBalon() {
		return futbolistasInterceptanBalon;
	}
	
	@Override
	public Oponente oponentePuedeInterceptarBalon() {
		return oponenteInterceptaBalon;
	}

	@Override
	public Futbolista futbolistaMasCercanoBalon() {
		return futbolistaMasCercano( balon.getPosicionBalon() );
	}
	
	@Override
	public Futbolista futbolistaMasCercano( PosicionCampo posicion ) {
		Futbolista futbolistaMasCercano = futbolistasOrdenados.get( 0 );
		for( int i = 1; i < futbolistasOrdenados.size(); i++ ) {
			if( futbolistasOrdenados.get( i ).getPosicion().distancia( posicion) < 
					futbolistaMasCercano.getPosicion().distancia( posicion ) ) {
				futbolistaMasCercano = futbolistasOrdenados.get( i );
			}
		}
		return futbolistaMasCercano;
	}
	
	@Override
	public Futbolista futbolistaMasCercano( PosicionCampo posicion, Futbolista descarte ) {
		ArrayList<Futbolista> descartes = new ArrayList<Futbolista>();
		descartes.add( descarte );
		return futbolistaMasCercano( posicion, descartes );
	}

	@Override
	public Futbolista futbolistaMasCercano( PosicionCampo posicion, ArrayList<Futbolista> descartes ) {
		Futbolista futbolistaMasCercano = null;
		
		for( int i = 0; i < futbolistasOrdenados.size(); i++ ) {
			if( futbolistaMasCercano == null && 
					descartes.indexOf( futbolistasOrdenados.get( i ) ) == -1 ) {
				
				futbolistaMasCercano = futbolistasOrdenados.get( i );
				
			} else if( descartes.indexOf( futbolistasOrdenados.get( i ) ) == -1 &&
					futbolistasOrdenados.get( i ).getPosicion().distancia( posicion) < 
					futbolistaMasCercano.getPosicion().distancia( posicion ) ) {
				
				futbolistaMasCercano = futbolistasOrdenados.get( i );
			}
		}
		return futbolistaMasCercano;
	}
	

	
	@Override
	public ArrayList<Futbolista> futbolistasMasCercanos( PosicionCampo posicion ) {
		ArrayList<Futbolista> descartes = new ArrayList<Futbolista>();
		ArrayList<Futbolista> masCercanos = new ArrayList<Futbolista>();
		
		for( int i = 0; i < futbolistasOrdenados.size(); i++ ) {
			Futbolista masCercano = futbolistaMasCercano( posicion, descartes );
			descartes.add( masCercano );
			masCercanos.add( masCercano );
		}
		
		return masCercanos;
	}

	@Override
	public Futbolista futbolistaBalonControlado() {
		return futbolistaBalonControlado;
	}

	@Override
	public Oponente oponenteMasCercanoBalon() {
		return oponenteMasCercano( balon.getPosicionBalon() );
	}
	
	@Override
	public Oponente oponenteMasCercano( PosicionCampo posicion ) {
		Oponente oponenteMasCercano = oponentes.get( 0 );
		for( int i = 0; i < oponentes.size(); i++ ) {
			if( oponentes.get( i ).getPosicion().distancia( posicion) < 
					oponenteMasCercano.getPosicion().distancia( posicion ) ) {
				oponenteMasCercano = oponentes.get( i );
			}
		}
		return oponenteMasCercano;
	}

	@Override
	public Balon getBalon() {
		return balon;
	}

	@Override
	public ArrayList<Futbolista> getFutbolistas() {
		return futbolistasOrdenados;
	}

	@Override
	public ArrayList<Oponente> getOponentes() {
		return oponentes;
	}

	@Override
	public void addSugerencia( Sugerencia anotacion ) {
		anotaciones.addSugerencia( anotacion );
	}

	@Override
	public Futbolista getFutbolista( int index ) {
		return futbolistas.get( index );
	}

	@Override
	public void addComando( Comando comando ) {
		comandos.add( comando );
	}
	
	public LinkedList<Comando> getComandos() {
		return comandos;
	}
	
	@Override
	public double getAtaque() {
		return ataque;
	}
	
	@Override
	public boolean saco() {
		return saco;
	}
	
	@Override
	public int iteracionesSaque() {
		return iteracionesSaque;
	}

	@Override
	public void disminuirRestriccionesPase() {
		for( int i = 0; i < futbolistas.size(); i++ ) {
			futbolistas.get( i ).disminuirRestriccionesPase();
		}
	}

	public void resetRestriccionesPase() {
		for( int i = 0; i < futbolistas.size(); i++ ) {
			futbolistas.get( i ).resetRestriccionesPase();
		}
	}

	

}
